#include <stdio.h>
#include "util.h"
#include "symbol.h"
#include "temp.h"
#include "assem.h"
#include "graph.h"
#include "flowgraph.h"

Temp_tempList FG_def(G_node n) {
	assert(n);
	AS_instrList *refInsList = G_nodeInfo(n);
	assert(refInsList);
	AS_instrList insList = *refInsList;
	assert(insList);
	AS_instr ins = insList->head;
	assert(ins);
	if(ins->kind == I_LABEL) 
		return NULL;
	return ins->u.OPER.dst;
}

Temp_tempList FG_use(G_node n) {
	assert(n);
	AS_instrList *refInsList = G_nodeInfo(n);
	assert(refInsList);
	AS_instrList insList = *refInsList;
	assert(insList);
	AS_instr ins = insList->head;
	assert(ins);
	if(ins->kind == I_LABEL) 
		return NULL;
	return ins->u.OPER.src;
}

bool FG_isMove(G_node n) {
	assert(n);
	AS_instrList *refInsList = G_nodeInfo(n);
	assert(refInsList);
	AS_instrList insList = *refInsList;
	assert(insList);
	AS_instr ins = insList->head;
	assert(ins);
	return ins->kind == I_MOVE;
}

bool FG_removeIns(G_node n) {
	// return whether the instruction corresponding to the node 'n' could be
	// removed
	// if that instruction could be removed, then modify the value of specified
	// pointer to skip that instruction and set the information field of the node
	// 'n' with nullptr
	
	// Instead of changing the information within node, we could remove all the
	// edges related to the node to mark that the instruction represented has
	// been eliminated
	assert(n);
	AS_instrList *refInsList = G_nodeInfo(n);
	assert(refInsList);
	AS_instrList insList = *refInsList;
	assert(insList);
	AS_instr ins = insList->head;
	assert(ins);
	switch(ins->kind) {
		case I_OPER: {
			if(!ins->u.OPER.dst)
				return FALSE; // normal jump or mult or div instruction
			if(ins->u.OPER.jumps)
				return FALSE; // function call
			break;
		}
		case I_LABEL: return FALSE;
		case I_MOVE: break;
	}	
	*refInsList = (*refInsList)->tail;
	//G_setNodeInfo(n, NULL); // mark the instruction has been removed
	// remove all the edge of the node
	for(G_nodeList p = G_pred(n); p; p = p->tail) {
		G_node pred = p->head;
		for(G_nodeList q = G_succ(n); q; q = q->tail) {
			G_node succ = q->head;
			G_addEdge(pred, succ);
		}
		G_rmEdge(pred, n);
	}
	for(G_nodeList q = G_succ(n); q; q = q->tail) {
		G_node succ = q->head;
		G_rmEdge(n, succ);
	}
	return TRUE;
}


static G_node traceGraph(G_graph g, AS_instrList *refInsList, S_table tab, G_nodeList *jumpList) {
	// In order to eliminate the unused definintion in the liveness analysis, I
	// decide to change the type of information in the node of control flow graph
	// to AS_instrList* to delete some instruction in the list conveniently.
	G_node node = G_Node(g, refInsList); // type of node->info is AS_instrList*
	assert(refInsList);
	AS_instrList insList = *refInsList;
	assert(insList);
	AS_instr ins = insList->head;
	assert(ins);
	if(ins->kind == I_LABEL) {
		// record label -> node mappings for JUMP and CJUMP instruction
		S_enter(tab, ins->u.LABEL.label, node);
	}
	if(ins->kind == I_OPER && ins->u.OPER.jumps) {
		// record all the graph nodes whose type of corresponding instruction is
		// JUMP and CJUMP 
		*jumpList = G_NodeList(node, *jumpList);
	}
	if(insList->tail)
		G_addEdge(node, traceGraph(g, &insList->tail, tab, jumpList));
	return node;
}

G_graph FG_AssemFlowGraph(AS_instrList *il) {
	G_graph g = G_Graph();
	S_table tab = S_empty();
	G_nodeList jumpList = NULL;
	// As the il->head must be a label instruction which would never be removed
	// during liveness analysis, so there is no need to pass a pointer to pointer
	// as the parameter.
	// However, the parameter 'il' would die after exiting FG_AssemFlowGraph() 
	// which means that the value of paramter 'il' may be substituted by local 
	// variables of other function.
	// The pointer to a parameter which has left the origin stack environment is 
	// similar to a wild pointer which points to a block of memory released.
	// What's worse, the parameter 'il' is also a pointer which would point to
	// some erratic memory after exiting FG_AssemFlowGraph() and lead to terrible
	// bugs.
	traceGraph(g, il, tab, &jumpList);
	for(G_nodeList jump = jumpList; jump; jump = jump->tail) {
		G_node from = jump->head;
		assert(from);
		AS_instrList *refInsList = G_nodeInfo(from);
		assert(refInsList);
		AS_instrList insList = *refInsList;
		assert(insList);
		AS_instr ins = insList->head;
		assert(ins);
		assert(ins->kind == I_OPER);
		assert(ins->u.OPER.jumps);
		Temp_labelList labels = ins->u.OPER.jumps->labels;
		assert(labels);
		G_node to = S_look(tab, labels->head);
		// According to the length of 'labels', to judge the kind of jump
		if(!labels->tail) {
			// unconditional jump
			G_nodeList succ = G_succ(from);
			if(succ && to) {
				// remove an edge
				G_rmEdge(from, succ->head);
			}
		}
		if(to)
			G_addEdge(from, to);
	}
	return g;		
}


